/**
 * @file 标签页分组工具
 * @description 提供标签页分组相关的工具函数，包括颜色系统和命名系统
 * @author 黄敏
 * @version 1.0.0
 * @since 2025-08-22
 */

import { isGroup } from './tab-position'
import { extractDomain } from '~shared/utils/urlUtils'

// 分组颜色循环系统
const GROUP_COLORS: string[] = [
  '#E74C3C',
  '#2ECC71',
  '#3498DB',
  '#9B59B6',
  '#F39C12',
  '#1ABC9C',
  '#34495E',
  '#E67E22',
  '#8E44AD',
  '#2980B9'
]

// 分组命名系统
let groupNameCounter = 1

/**
 * 根据分组ID获取对应的颜色
 * @param groupId 分组ID
 * @returns 对应的颜色值
 */
export function getGroupColorById(groupId: number): string {
  // 使用分组ID对颜色数组长度取模，确保相同ID总是得到相同颜色
  const len = GROUP_COLORS.length
  const colorIndex = ((groupId % len) + len) % len
  const defaultColor = len > 0 ? (GROUP_COLORS[0] as string) : '#2980B9'
  return GROUP_COLORS[colorIndex] ?? defaultColor
}

/**
 * 获取下一个分组名称
 * @returns 分组名称
 */
export function getNextGroupName(): string {
  const name = `Group ${groupNameCounter}`
  groupNameCounter++
  return name
}
/**
 * 打开分组中下一个等待的标签页
 * 支持不同域名的标签页并发打开，同域名的标签页串行打开
 * @param group 标签页分组
 * @param opts 可选参数
 */
export function openGroupNewTab(
  group: F2.tab.Group,
  opts?: { skipDomain?: string; onlyDomain?: string; maxConcurrent?: number }
): void {
  // 初始化 openingDomains 和 tabIdToDomain
  if (!group.openingDomains) {
    group.openingDomains = new Set<string>()
  }
  if (!group.tabIdToDomain) {
    group.tabIdToDomain = new Map<number, string>()
  }

  // 并发容量：遵循上限（若提供）与当前已在打开中的域名数
  const currentOpen = group.openingDomains.size
  const maxConcurrentFromStorage = (() => {
    try {
      const s = localStorage.getItem('tab.maxCrossDomainConcurrency')
      const n = s ? Number(s) : NaN
      return Number.isFinite(n) ? Math.max(1, n) : undefined
    } catch {
      return undefined
    }
  })()
  const maxConcurrent = opts?.maxConcurrent ?? maxConcurrentFromStorage
  let capacity =
    typeof maxConcurrent === 'number'
      ? Math.max(0, maxConcurrent - currentOpen)
      : Number.POSITIVE_INFINITY

  // 查找第一个不在打开中的域名的等待标签页（跨域并发，受容量限制；同域始终串行）
  const adds: F2.tab.CreateWithId[] = []
  for (let i = 0; i < group.waitingTabs.length && capacity > 0; i++) {
    const waitTab = group.waitingTabs[i]
    if (!waitTab) continue

    const domain = extractDomain(waitTab.url)

    // 过滤：只开特定域或跳过特定域
    if (opts?.onlyDomain && domain !== opts.onlyDomain) continue
    if (opts?.skipDomain && domain === opts.skipDomain) continue

    // 如果该域名没有正在打开的标签页，则打开这个标签页
    if (!group.openingDomains.has(domain)) {
      group.openingDomains.add(domain)
      // 记录标签页ID到原始域名的映射（用于处理URL重定向）
      group.tabIdToDomain.set(waitTab.id, domain)
      adds.push(toRaw(waitTab))
      capacity--
      // 继续查找其他可以并发打开的域名，直到容量耗尽
    }
  }
  if (adds.length > 0) _pre_.browser.addTab(adds)
}

/**
 * 创建新的标签页分组
 * @param tabs 标签页或分组数组
 * @param urls 要分组的标签页URL数组
 * @param title 分组标题（可选）
 * @param color 分组颜色（可选）
 * @param insertIndex 插入位置（可选）
 * @returns 新创建的分组ID
 */
export function createGroup(
  tabs: F2.tab.TabOrGroup[],
  urls: string[],
  title?: string,
  color?: string,
  insertIndex?: number
): number {
  const id = _pre_.generateTabId()
  const group: F2.tab.Group = {
    id,
    type: 'group',
    title: title || getNextGroupName(), // 如果没有指定标题，自动使用下一个名称
    color: color || getGroupColorById(id), // 根据分组ID获取对应颜色
    collapsed: false,
    isOpening: false,
    tabs: [],
    waitingTabs: urls.map(url => ({
      id: _pre_.generateTabId(),
      title: '',
      url
    })),
    openingDomains: new Set<string>(), // 初始化域名跟踪集合
    tabIdToDomain: new Map<number, string>() // 初始化标签页ID到域名的映射
  }

  if (typeof insertIndex === 'number' && insertIndex >= 0 && insertIndex <= tabs.length) {
    tabs.splice(insertIndex, 0, group)
  } else {
    tabs.push(group)
  }

  openGroupNewTab(group)

  return group.id
}

/**
 * 从分组移除tab（移回顶层）
 * @param tabs 标签页或分组数组
 * @param groupId 分组id
 * @param tabIds 要移除的tab id数组
 * @param insertIndex 插入到顶层tabs的索引（可选，默认为原分组位置）
 */
export function removeTabsFromGroup(
  tabs: F2.tab.TabOrGroup[],
  groupId: number,
  tabIds: number[],
  insertIndex?: number
): void {
  // 1. 找到目标分组
  const groupIndex = tabs.findIndex(t => isGroup(t) && t.id === groupId)
  if (groupIndex === -1) return
  const group = tabs[groupIndex] as F2.tab.Group

  // 2. 从group.tabs中移除tabIds对应的tab
  const removedTabs: F2.tab.Entity[] = []
  group.tabs = group.tabs.filter(tab => {
    if (tabIds.includes(tab.id)) {
      removedTabs.push(tab)
      return false
    }
    return true
  })

  // 3. 确定插入位置
  let targetInsertIndex: number
  if (typeof insertIndex === 'number' && insertIndex >= 0) {
    targetInsertIndex = insertIndex
  } else {
    // 默认插入到原分组位置
    targetInsertIndex = groupIndex
  }

  // 4. group被移空后的处理
  if (group.tabs.length === 0) {
    if (group.waitingTabs.length === 0) {
      tabs.splice(groupIndex, 1)
      // 如果分组被移除且插入位置在分组之后，需要调整插入位置
      if (targetInsertIndex > groupIndex) {
        targetInsertIndex--
      }
    } else {
      // 分组内暂无可见 tab 但仍有等待项，确保继续推进
      openGroupNewTab(group)
    }
  }

  // 5. 插入到顶层tabs指定位置
  if (removedTabs.length > 0) {
    // 确保插入位置有效
    targetInsertIndex = Math.max(0, Math.min(targetInsertIndex, tabs.length))
    tabs.splice(targetInsertIndex, 0, ...removedTabs)
  }
}

/**
 * 切换分组的折叠状态
 * @param tabs 标签页或分组数组
 * @param groupId 分组ID
 * @returns 切换后的折叠状态
 */
export function toggleGroupCollapsed(tabs: F2.tab.TabOrGroup[], groupId: number): boolean | null {
  const group = tabs.find(t => isGroup(t) && t.id === groupId) as F2.tab.Group | undefined
  if (group) {
    group.collapsed = !group.collapsed
    return group.collapsed
  }
  return null
}

/**
 * 更新分组信息
 * @param tabs 标签页或分组数组
 * @param groupId 分组ID
 * @param updates 要更新的字段
 */
export function updateGroup(
  tabs: F2.tab.TabOrGroup[],
  groupId: number,
  updates: Partial<Pick<F2.tab.Group, 'title' | 'color' | 'collapsed'>>
): void {
  const group = tabs.find(t => isGroup(t) && t.id === groupId) as F2.tab.Group | undefined
  if (group) {
    Object.assign(group, updates)
  }
}

/**
 * 获取分组中的所有标签页
 * @param tabs 标签页或分组数组
 * @param groupId 分组ID
 * @returns 分组中的标签页数组
 */
export function getGroupTabs(tabs: F2.tab.TabOrGroup[], groupId: number): F2.tab.Entity[] {
  const group = tabs.find(t => isGroup(t) && t.id === groupId) as F2.tab.Group | undefined
  return group ? group.tabs : []
}

/**
 * 检查分组是否为空
 * @param tabs 标签页或分组数组
 * @param groupId 分组ID
 * @returns 是否为空
 */
export function isGroupEmpty(tabs: F2.tab.TabOrGroup[], groupId: number): boolean {
  const group = tabs.find(t => isGroup(t) && t.id === groupId) as F2.tab.Group | undefined
  return !group || group.tabs.length === 0
}
